#include <stdio.h>
#include <conio.h>
#include <cpar.h>

static CPARContext *pGlobalContext;

typedef struct CPARGlobalStackData CPARGlobalStackData;

struct CPARGlobalStackData
{
  CPARMutex *buffer;
  char      c;
};

static CPARGlobalStackData *pGlobalStack;


char ler(CPARContext *pContext)
{
  char ch;

  ch = 0;

  if(pGlobalStack->c != 0)
  {
    ch= pGlobalStack->c;
    pGlobalStack->c = 0;
  }

  return ch;
}

char CPARMonitor_buffer_ler(CPARContext *pContext)
{
  char ch;

  CPARMutexLock(pGlobalStack->buffer);
  ch = ler(pContext);
  CPARMutexUnlock(pGlobalStack->buffer);

  return ch;
}

void escrever(char carac)
{
  if(pGlobalStack->c==0)
  {
    pGlobalStack->c=carac;
  }
}

void CPARMonitor_buffer_escrever(CPARContext *pContext, char carac)
{
  CPARMutexLock(pGlobalStack->buffer);
  escrever(carac);
  CPARMutexUnlock(pGlobalStack->buffer);
}

void CPARTask_produtor(CPARContext *pContext);
void CPARTask_produtor(CPARContext *pContext)
{
  char x;
  do
  {
    x=_getch();
    CPARMonitor_buffer_escrever(pContext,x);
  } while (x!='.');
}

void CPARTask_consumidor(CPARContext *pContext);
void CPARTask_consumidor(CPARContext *pContext)
{
  char x;
  while ( (x=CPARMonitor_buffer_ler(pContext)) != '.')
  {
    if(x > 0)
      putchar(x);
  }
}

int main(int argc, char **argv)
{
  CPARInit();

  if( CPARContextCreate( NULL, &pGlobalContext, sizeof(CPARGlobalStackData) ))
  {
    CPAR_ERROR("Falha ao alocar contexto global");
    return CPAR_E_NO_MEMORY;
  }
  pGlobalStack = (CPARGlobalStackData*) CPARContextStackGet(pGlobalContext);

  if( CPARContextWorkPoolCreate( pGlobalContext, 2 ))
  {
    CPAR_ERROR("Falha ao alocar work pool");
    return -1;
  }

  CPARMutexCreate(CPARContextAllocatorGet(pGlobalContext), &(pGlobalStack->buffer));

  CPARContextTaskDispatch(pGlobalContext, 1, CPARTask_produtor, 0);
  CPARContextTaskDispatch(pGlobalContext, 1, CPARTask_consumidor, 0);

  CPARContextTaskWaitAll(pGlobalContext);

  CPARMutexDestroy(pGlobalStack->buffer);

  return CPAR_SUCCESS;
}
